﻿using System;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace MyChatRoom
{
    /// <summary>
    /// 服务端和客户端都要使用的一个类
    /// </summary>
    public class StateObject
    {
        public Socket workSocket = null;
        public const int BufferSize = 1024;
        public byte[] buffer = new byte[BufferSize];
        public StringBuilder sb = new StringBuilder();
    }

    /// <summary>
    /// 异步TCP服务端套接字  - 类中方法都是静态方法，可自行按需要修改为非静态
    /// </summary>
    public class AsynchronousSocketListener
    {
        public static ManualResetEvent allDone = new ManualResetEvent(false);
        /// <summary>
        /// 服务端构造函数 - 可自由发挥
        /// </summary>
        public AsynchronousSocketListener()
        {
        }
        /// <summary>
        /// 服务端启动监听方法 - 建议看懂方法的逻辑结构
        /// </summary>
        /// <param name="IP">主机IP</param>
        /// <param name="port">端口号</param>
        /// <param name="MaxLisstenNum">允许最大同时接入数量</param>
        public static void StartListening(string IP, int port, int MaxLisstenNum)
        {
            byte[] bytes = new Byte[1024];
            IPAddress ipAddress = IPAddress.Parse(IP);
            IPEndPoint localEndPoint = new IPEndPoint(ipAddress, port);
            // 创建TCP协议的Socket套接字
            Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            try
            {
                listener.Bind(localEndPoint);
                listener.Listen(MaxLisstenNum);
                //注意，这里是个无限循环，建议使用线程调用此方法，否则UI会阻塞
                while (true)
                {
                    allDone.Reset();
                    //等待请求
                    listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);
                    allDone.WaitOne();
                }
            }
            catch (Exception)
            {

            }
        }
        public static void AcceptCallback(IAsyncResult ar)
        {
            allDone.Set();
            Socket listener = (Socket)ar.AsyncState;
            Socket handler = listener.EndAccept(ar);
            StateObject state = new StateObject();
            state.workSocket = handler;
            handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                                 new AsyncCallback(ReadCallback), state);
        }
        public static void ReadCallback(IAsyncResult ar)
        {
            String content = String.Empty;
            StateObject state = (StateObject)ar.AsyncState;
            Socket handler = state.workSocket;
            int bytesRead = handler.EndReceive(ar);
            if (bytesRead > 0)
            {
                state.sb.Append(Encoding.Unicode.GetString(state.buffer, 0, bytesRead));
                content = state.sb.ToString();
                //注意这个<EOF>,是个结尾控制符，客户端发送字符串的时候，记得尾部加上<EOF>表示结尾
                if (content.IndexOf("<EOF>") > -1)
                {
                    //服务端接收到客户端的消息以后，返回相同内容给客户端，表示接收成功
                    Console.WriteLine("Read {0} bytes from socket. \n Data : {1}", content.Length, content);
                    Send(handler, content);
                }
                else
                {
                    handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                                         new AsyncCallback(ReadCallback), state);
                }
            }
        }
        private static void Send(Socket handler, String data)
        {
            byte[] byteData = Encoding.Unicode.GetBytes(data);
            handler.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), handler);
        }
        private static void SendCallback(IAsyncResult ar)
        {
            try
            {
                Socket handler = (Socket)ar.AsyncState;
                int bytesSent = handler.EndSend(ar);
                //输出一下发送给客户端的字节数
                Console.WriteLine("Sent {0} bytes to client.", bytesSent);
                handler.Shutdown(SocketShutdown.Both);
                handler.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
    }


    /// <summary>
    /// 异步客户端套接字 - 类中方法都是静态方法，可自行按需要修改为非静态
    /// </summary>
    public class AsynchronousClient
    {
        //端口号
        private static int port = 12345;
        private static ManualResetEvent connectDone =
            new ManualResetEvent(false);
        private static ManualResetEvent sendDone =
            new ManualResetEvent(false);
        private static ManualResetEvent receiveDone =
            new ManualResetEvent(false);
        // The response from the remote device.
        private static String response = String.Empty;
        /// <summary>
        /// 客户端连接服务端方法 - 建议看懂方法的逻辑结构
        /// </summary>
        /// <param name="IP">主机IP</param>
        /// <param name="pt">端口号</param>
        /// <param name="SendString">向主机发送的内容</param>
        public static void StartClient(string IP, int pt, string SendString)
        {
            //<EOF> 表示告诉服务端这里是结尾
            SendString = SendString + "<EOF>";
            port = pt;
            // Connect to a remote device.
            try
            {
                IPAddress ipAddress = IPAddress.Parse(IP);
                IPEndPoint remoteEP = new IPEndPoint(ipAddress, port);
                // 创建客户端TCP套接字
                Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                // 登陆主机
                client.BeginConnect(remoteEP,
                                    new AsyncCallback(ConnectCallback), client);
                connectDone.WaitOne();
                // 发送消息到主机
                Send(client, SendString);
                sendDone.WaitOne();
                // 发送完判断主机是否收到消息，这是TCP协议的特点，可以进行多次握手
                Receive(client);
                receiveDone.WaitOne();
                // 输出服务端的返回内容 默认发送什么就返回什么
                Console.WriteLine("Response received : {0}", response);
                //
                client.Shutdown(SocketShutdown.Both);
                client.Close();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
        private static void ConnectCallback(IAsyncResult ar)
        {
            try
            {
                Socket client = (Socket)ar.AsyncState;
                client.EndConnect(ar);
                connectDone.Set();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
        private static void Receive(Socket client)
        {
            try
            {
                StateObject state = new StateObject();
                state.workSocket = client;
                client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
                                    new AsyncCallback(ReceiveCallback), state);
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
        private static void ReceiveCallback(IAsyncResult ar)
        {
            try
            {
                StateObject state = (StateObject)ar.AsyncState;
                Socket client = state.workSocket;
                int bytesRead = client.EndReceive(ar);
                if (bytesRead > 0)
                {
                    state.sb.Append(Encoding.Unicode.GetString(state.buffer, 0, bytesRead));
                    client.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReceiveCallback), state);
                }
                else
                {
                    if (state.sb.Length > 1)
                    {
                        response = state.sb.ToString();
                    }
                    receiveDone.Set();
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
        private static void Send(Socket client, String data)
        {
            byte[] byteData = Encoding.Unicode.GetBytes(data);
            client.BeginSend(byteData, 0, byteData.Length, 0, new AsyncCallback(SendCallback), client);
        }
        private static void SendCallback(IAsyncResult ar)
        {
            try
            {
                Socket client = (Socket)ar.AsyncState;
                int bytesSent = client.EndSend(ar);
                sendDone.Set();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.ToString());
            }
        }
    }


}
